/*
 *Copyright 2021-2023 NERCIS
 *
 *Licensed under the Apache License, Version 2.0 (the "License");
 *you may not use this file except in compliance with the License.
 *You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 *Unless required by applicable law or agreed to in writing, software
 *distributed under the License is distributed on an "AS IS" BASIS,
 *WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *See the License for the specific language governing permissions and
 *limitations under the License.
 */

package cn.ac.nercis.pes.service.project.imported;

import cn.ac.nercis.pes.common.constant.common.BaseConstants;
import cn.ac.nercis.pes.model.dto.common.Result;
import cn.ac.nercis.pes.model.dto.project.ImportConflictDTO;
import cn.ac.nercis.pes.model.standard.asset.AssetSDTO;
import cn.ac.nercis.pes.model.standard.evaluation.EvaluationRecordSDTO;
import cn.ac.nercis.pes.repository.dal.evaluation.AssetEvaluationMethodRepository;
import cn.ac.nercis.pes.repository.dal.evaluation.AssetRepository;
import cn.ac.nercis.pes.repository.dal.evaluation.EvaluationRecordRepository;
import cn.ac.nercis.pes.repository.dal.project.SystemTargetRepository;
import cn.ac.nercis.pes.repository.model.evaluation.AssetDO;
import cn.ac.nercis.pes.repository.model.evaluation.AssetEvaluationMethodDO;
import cn.ac.nercis.pes.repository.model.evaluation.EvaluationRecordDO;
import cn.ac.nercis.pes.repository.model.project.SystemTargetDO;
import cn.ac.nercis.pes.service.utils.AssetUtils;
import cn.ac.nercis.pes.service.utils.CheckFieldUtils;
import lombok.NonNull;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.time.LocalDateTime;
import java.util.*;
import java.util.stream.Collectors;

/**
 * 现场测评记录信息导入服务
 *
 * @author zpy
 * @version 1.0.0
 */
@Slf4j
@Service
public class EvaluationRecordImportService extends CommonImportService {
    private final EvaluationRecordRepository evaluationRecordRepository;
    private final AssetRepository assetRepository;
    private final AssetEvaluationMethodRepository assetEvaluationMethodRepository;
    private final SystemTargetRepository systemTargetRepository;

    @Autowired
    public EvaluationRecordImportService(EvaluationRecordRepository evaluationRecordRepository,
                                         AssetRepository assetRepository,
                                         AssetEvaluationMethodRepository assetEvaluationMethodRepository,
                                         SystemTargetRepository systemTargetRepository) {
        this.evaluationRecordRepository = evaluationRecordRepository;
        this.assetRepository = assetRepository;
        this.assetEvaluationMethodRepository = assetEvaluationMethodRepository;
        this.systemTargetRepository = systemTargetRepository;
    }

    public Result<List<ImportConflictDTO>> checkEvaluationRecord(@NonNull String systemId, List<EvaluationRecordSDTO> evaluationRecords, List<AssetSDTO> assets) {
        List<EvaluationRecordDO> sourceData = this.evaluationRecordRepository.findAllBySystemId(systemId);
        if (CollectionUtils.isEmpty(sourceData)) {
            return Result.success();
        }
        List<AssetDO> sourceAssets = this.assetRepository.findAllBySystemId(systemId);
        List<AssetEvaluationMethodDO> sourceMethods = this.assetEvaluationMethodRepository.findAllBySystemId(systemId);
        List<SystemTargetDO> systemTargets = this.systemTargetRepository.findAllBySystemId(systemId);
        List<ImportConflictDTO> conflictData = evaluationRecords.stream()
                .flatMap(ec -> this.checkEvaluationRecord(ec, sourceData, systemTargets, findEvaluationMethod(ec, assets, sourceAssets, sourceMethods)).stream())
                .filter(Objects::nonNull)
                .collect(Collectors.toList());
        return Result.success(conflictData);
    }

    public Result<String> importEvaluationRecord(@NonNull String systemId, List<EvaluationRecordSDTO> evaluationRecords, List<AssetSDTO> assets, Boolean cover) {
        List<EvaluationRecordDO> sourceData = this.evaluationRecordRepository.findAllBySystemId(systemId);
        List<AssetDO> sourceAssets = this.assetRepository.findAllBySystemId(systemId);
        List<AssetEvaluationMethodDO> sourceMethods = this.assetEvaluationMethodRepository.findAllBySystemId(systemId);
        Result<String> result = Result.success();
        for (EvaluationRecordSDTO item : evaluationRecords) {
            var method = findEvaluationMethod(item, assets, sourceAssets, sourceMethods);
            if(Objects.isNull(method)){
                continue;
            }
            result = this.importEvaluationRecord(systemId, item, sourceData, cover, method);
            if (!result.isSuccess()) {
                break;
            }
        }
        return result;
    }

    private List<ImportConflictDTO> checkEvaluationRecord(@NonNull EvaluationRecordSDTO importData, List<EvaluationRecordDO> sourceData,
                                                          List<SystemTargetDO> systemTargets, AssetEvaluationMethodDO evaluationMethod) {
        if (Objects.isNull(evaluationMethod)) {
            return Collections.emptyList();
        }
        EvaluationRecordDO entity = sourceData.stream()
                .filter(st -> StringUtils.equals(st.getAssetEvaMethodId(), evaluationMethod.getId())
                        && StringUtils.equals(st.getAsset().getId(), evaluationMethod.getAssetId()))
                .findFirst().orElse(null);
        if (Objects.isNull(entity)) {
            return Collections.emptyList();
        }
        List<ImportConflictDTO> conflictData = new ArrayList<>();
        if (importData.getConclusion() != entity.getConclusion()) {
            conflictData.add(ImportConflictDTO.builder()
                    .fieldName("对象测评结论")
                    .oldContent(entity.getConclusion().getName())
                    .newContent(importData.getConclusion().getName())
                    .build());
        }
        if (importData.getDResult() != entity.getDResult()) {
            conflictData.add(ImportConflictDTO.builder()
                    .fieldName("D）测评结论")
                    .oldContent(entity.getDResult().getName())
                    .newContent(importData.getDResult().getName())
                    .build());
        }
        if (importData.getAResult() != entity.getAResult()) {
            conflictData.add(ImportConflictDTO.builder()
                    .fieldName("A）测评结论")
                    .oldContent(entity.getAResult().getName())
                    .newContent(importData.getAResult().getName())
                    .build());
        }
        if (importData.getACondition() != entity.getACondition()) {
            conflictData.add(ImportConflictDTO.builder()
                    .fieldName("A）使用情况")
                    .oldContent(entity.getACondition().getName())
                    .newContent(importData.getACondition().getName())
                    .build());
        }
        if (importData.getKResult() != entity.getKResult()) {
            conflictData.add(ImportConflictDTO.builder()
                    .fieldName("K）测评结论")
                    .oldContent(entity.getKResult().getName())
                    .newContent(importData.getKResult().getName())
                    .build());
        }
        if (importData.getKCondition() != entity.getKCondition()) {
            conflictData.add(ImportConflictDTO.builder()
                    .fieldName("K）安全情况")
                    .oldContent(entity.getKCondition().getName())
                    .newContent(importData.getKCondition().getName())
                    .build());
        }
        conflictData.addAll(CheckFieldUtils.checkField(entity, importData));
        if (CollectionUtils.isNotEmpty(conflictData)) {
            String title = String.format("%s(%s)", AssetUtils.getAssetName(entity.getAsset()), getTargetName(importData.getUnitTargetId(), systemTargets));
            conflictData.add(0, ImportConflictDTO.builder()
                    .fieldName(BaseConstants.IMPORT_CONFLICT_DATA_TAG)
                    .newContent(title)
                    .oldContent(title)
                    .build());
        }
        return conflictData;
    }

    private Result<String> importEvaluationRecord(String systemId, @NonNull EvaluationRecordSDTO importData, List<EvaluationRecordDO> sourceData, Boolean cover,
                                                  @NonNull AssetEvaluationMethodDO evaluationMethod) {
        EvaluationRecordDO entity = sourceData.stream()
                .filter(st -> StringUtils.equals(st.getAssetEvaMethodId(), evaluationMethod.getId())
                        && StringUtils.equals(st.getAsset().getId(), evaluationMethod.getAssetId()))
                .findFirst()
                .orElse(null);
        if (Objects.isNull(entity)) {
            return createEvaluationRecord(systemId, importData, evaluationMethod,sourceData);
        } else {
            return modifyEvaluationRecord(entity, importData, cover);
        }
    }

    private Result<String> createEvaluationRecord(String systemId, EvaluationRecordSDTO importData, AssetEvaluationMethodDO evaluationMethod,
                                                  List<EvaluationRecordDO> sourceData) {
        var target = systemTargetRepository.findBySystemIdAndKmTargetId(systemId,evaluationMethod.getUnitTargetId()).orElse(null);
        if (Objects.isNull(target)) {
            return Result.failed("导入现场测评记录信息失败，未获取到指定单元指标信息");
        }
        AssetDO assetDO = assetRepository.findById(evaluationMethod.getAssetId()).orElse(null);
        if (Objects.isNull(assetDO)) {
            return Result.failed("导入现场测评记录信息失败，未获取到指定对象信息");
        }
        EvaluationRecordDO entity = EvaluationRecordDO.builder()
                .systemId(systemId)
                .layerTargetId(importData.getLayerTargetId())
                .assetEvaMethodId(evaluationMethod.getId())
                .unitTargetId(target.getKmTargetId())
                .asset(assetDO)
                .conclusion(importData.getConclusion())
                .dResult(importData.getDResult())
                .aResult(importData.getAResult())
                .aCondition(importData.getACondition())
                .kResult(importData.getKResult())
                .kCondition(importData.getKCondition())
                .dContent(importData.getDContent())
                .aContent(importData.getAContent())
                .kContent(importData.getKContent())
                .content(importData.getContent())
                .dFiles(importData.getDFiles())
                .aFiles(importData.getAFiles())
                .kFiles(importData.getKFiles())
                .manageFiles(importData.getManageFiles())
                .score(importData.getScore())
                .finishState(importData.getFinishState())
                .build();
        entity.setCreateTime(LocalDateTime.now());
        return Optional.of(entity)
                .map(this.evaluationRecordRepository::save)
                .map(er->{
                    sourceData.add(er);
                    return Result.success(er.getId());
                })
                .orElse(Result.failed("导入现场测评记录信息，创建失败"));
    }

    private Result<String> modifyEvaluationRecord(EvaluationRecordDO sourceData, EvaluationRecordSDTO importData, Boolean cover) {
        sourceData.setConclusion(CheckFieldUtils.applyContent(sourceData.getConclusion(), importData.getConclusion(), cover));
        sourceData.setDResult(CheckFieldUtils.applyContent(sourceData.getDResult(), importData.getDResult(), cover));
        sourceData.setAResult(CheckFieldUtils.applyContent(sourceData.getAResult(), importData.getAResult(), cover));
        sourceData.setACondition(CheckFieldUtils.applyContent(sourceData.getACondition(), importData.getACondition(), cover));
        sourceData.setKResult(CheckFieldUtils.applyContent(sourceData.getKResult(), importData.getKResult(), cover));
        sourceData.setKCondition(CheckFieldUtils.applyContent(sourceData.getKCondition(), importData.getKCondition(), cover));
        sourceData.setDContent(CheckFieldUtils.applyContent(sourceData.getDContent(), importData.getDContent(), cover));
        sourceData.setAContent(CheckFieldUtils.applyContent(sourceData.getAContent(), importData.getAContent(), cover));
        sourceData.setKContent(CheckFieldUtils.applyContent(sourceData.getKContent(), importData.getKContent(), cover));
        sourceData.setContent(CheckFieldUtils.applyContent(sourceData.getContent(), importData.getContent(), cover));
        sourceData.setDFiles(CheckFieldUtils.applyContent(sourceData.getDFiles(), importData.getDFiles(), cover));
        sourceData.setAFiles(CheckFieldUtils.applyContent(sourceData.getAFiles(), importData.getAFiles(), cover));
        sourceData.setKFiles(CheckFieldUtils.applyContent(sourceData.getKFiles(), importData.getKFiles(), cover));
        sourceData.setManageFiles(CheckFieldUtils.applyContent(sourceData.getManageFiles(), importData.getManageFiles(), cover));
        sourceData.setScore(CheckFieldUtils.applyContent(sourceData.getScore(), importData.getScore(), cover));
        sourceData.setFinishState(CheckFieldUtils.applyContent(sourceData.getFinishState(), importData.getFinishState(), cover));
        sourceData.setModifyTime(LocalDateTime.now());
        return Optional.of(sourceData)
                .map(this.evaluationRecordRepository::save)
                .map(EvaluationRecordDO::getId)
                .map(Result::success)
                .orElse(Result.failed("导入现场测评记录信息，修改失败"));
    }

    private AssetEvaluationMethodDO findEvaluationMethod(@NonNull EvaluationRecordSDTO record, List<AssetSDTO> importAssets,
                                                         List<AssetDO> sourceAssets, List<AssetEvaluationMethodDO> sourceMethods) {
        AssetDO assetDO = findAsset(record.getAssetId(), importAssets, sourceAssets);
        if (Objects.isNull(assetDO)) {
            return null;
        }
        return sourceMethods.stream()
                .filter(st -> StringUtils.equals(st.getClassId(), assetDO.getClassId())
                        && StringUtils.equals(st.getAssetId(), assetDO.getId())
                        && StringUtils.equals(st.getUnitTargetId(), record.getUnitTargetId()))
                .findFirst()
                .orElse(null);
    }
}
